__author__ = 'xiaosu'
# -*- coding: utf-8 -*-

from flask import Flask,render_template,redirect,flash,url_for
from flask import request
from flask import g,session
from octopus import app,db,lm,cache
from forms import HostForm,ClusterForm,RunCommandForm,EditMultiHostForm,RegisterForm,LoginForm,CommandTypeForm,CommandForm
import add_host
from models import Clusters,Hosts,Users,users_clusters,Audit,CommandType,Command
from sqlalchemy.exc import IntegrityError
from settings import CLUSTERS_PER_PAGE,HOSTS_PER_PAGE,USERS_PER_PAGE,COMMANDTYPE_PER_PAGE,COMMAND_PER_PAGE
from flask import json
import run_a_command
from flask_login import login_user,logout_user,current_user,login_required
from hashlib import md5
from sqlalchemy import select
import datetime

from flask_sqlalchemy import get_debug_queries

def save_audit_log(audit_log):
    try:
        db.session.add(audit_log)
        db.session.commit()
    except Exception as e:
        app.logger.error(str(e))
        db.session.rollback()
    finally:
        db.session.close()


@lm.user_loader
def load_user(id):
    return Users.query.get(int(id))

@app.before_request
def before_request():
    g.user = current_user


@app.route('/')
@app.route('/index')
@login_required
def index():
    return redirect(url_for('run_command'))
    # return render_template("index.html",title="Hello")

@app.route('/manage_clusters')
@app.route('/manage_clusters/<int:page>')
@login_required
def manage_clusters(page = 1):
    if not g.user.is_admin:
        return  redirect(url_for('index'))
    form = ClusterForm()
    clusters=Clusters.query.paginate(page,CLUSTERS_PER_PAGE,False)
    return render_template("manage_clusters.html",title="manage clusters" , form=form,clusters=clusters)

@app.route('/manage_users')
@app.route('/manage_users/<int:page>')
@login_required
def manage_users(page = 1):
    if not g.user.is_admin:
        return  redirect(url_for('index'))
    form = RegisterForm()
    users=Users.query.paginate(page,USERS_PER_PAGE,False)
    clusters = Clusters.query.all()
    return render_template("manage_users.html",title="manage users" , registerform=form,users=users,clusters=clusters)

@app.route('/manage_hosts')
@app.route('/manage_hosts/<int:page>')
@app.route('/manage_hosts/<int:page>/<int:clusterid>')
@login_required
def manage_hosts(page = 1,clusterid=0):
    if not g.user.is_admin:
        return  redirect(url_for('index'))
    form = HostForm()
    clusters=Clusters.query.all()
    if clusterid == 0:
        hosts=Hosts.query.order_by(Hosts.full_ip).paginate(page,HOSTS_PER_PAGE,False)
    else:
        hosts=Hosts.query.filter_by(clusterid = clusterid).order_by(Hosts.full_ip).paginate(page,HOSTS_PER_PAGE,False)
    return render_template("manage_hosts.html",title="manage hosts" , form=form,hosts=hosts,clusters=clusters,clusterid=clusterid)



@app.route('/add_one_cluster',methods=['POST'])
@login_required
def add_one_cluster():
    if not g.user.is_admin:
        return  redirect(url_for('index'))
    form = ClusterForm()
    if form.validate_on_submit():
        clustername = form.clustername.data
        runuser = form.runuser.data
        comment = form.comment.data
        enabled = form.enabled.data
        audit = form.audit.data
        newcluster = Clusters(clustername,runuser,audit,enabled,comment=comment)
        try:
            db.session.add(newcluster)
            db.session.commit()
            return "success"
        except Exception as e:
            app.logger.error(str(e))
            db.session.rollback()
            if str(e).find("column clustername is not unique") != -1:
                return "cluster '"+clustername+"' is already exist in database"
            else:
                print "unknown error",e
                return "unknonw error,please check the log"
        finally:
            db.session.close()
    else:
        return redirect(url_for("manage_clusters"))

@app.route('/edit_one_cluster/<int:cluster_id>',methods=['POST'])
@login_required
def edit_one_cluster(cluster_id):
    if not g.user.is_admin:
        return  redirect(url_for('index'))
    form = ClusterForm()
    new = Clusters.query.get(int(cluster_id))
    if new == None:
        flash("Sorry! cluster  not found",'error')
        return redirect(url_for('manage_clusters'))
    if form.validate_on_submit():
        new.clustername = form.clustername.data
        new.runuser = form.runuser.data
        new.comment = form.comment.data
        new.enabled = form.enabled.data
        new.audit = form.audit.data
        try:
            db.session.commit()
            return "success"
        except Exception as e:
            app.logger.error(str(e))
            db.session.rollback()
            if str(e).find("column clustername is not unique") != -1:
                return "clustername '"+form.clustername.data+"' is already exist in database"
            else:
                return "unknonw error,please check the log"
        finally:
            db.session.close()
    else:
        return redirect(url_for("manage_clusters"))




@app.route('/delete_one_cluster/<int:cluster_id>',methods=['GET'])
@login_required
def delete_one_cluster(cluster_id):
    if not g.user.is_admin:
        return  redirect(url_for('index'))
    cluster=Clusters.query.get(int(cluster_id))
    if Hosts.query.filter_by(clusterid=cluster_id).count() > 0:
        flash("Plese delete hosts in cluster "+cluster.clustername+" first",'error')
        return redirect(url_for('manage_clusters'))
    if cluster == None:
        flash("Sorry! cluster  not found",'error')
        return redirect(url_for('manage_clusters'))
    db.session.delete(cluster)
    audit_log=Audit(g.user.userid,4,g.user.loginname + " delete cluster "+ cluster.clustername)
    try:
        db.session.commit()
        save_audit_log(audit_log)
        flash("cluster "+ cluster.clustername +" deleted successfully",'ok')
    except Exception as e:
        app.logger.error(str(e))
        db.session.rollback()
        flash("Sorry! cluster delete failed",'error')
    finally:
        db.session.close()
    return redirect(url_for('manage_clusters'))


@app.route('/delete_one_user/<int:user_id>',methods=['GET'])
@login_required
def delete_one_user(user_id):
    if not g.user.is_admin:
        return  redirect(url_for('index'))
    try:
        user=Users.query.get(int(user_id))
        if user == None:
            flash("Sorry! user  not found",'error')
            return redirect(url_for('manage_users'))
        db.session.delete(user)
        db.session.commit()
        flash("user "+ user.loginname +" deleted successfully",'ok')
    except Exception as e:
        app.logger.error(str(e))
        db.session.rollback()
        flash("Sorry! delete failed ! Please check the log",'error')
    finally:
        db.session.close()
    return redirect(url_for('manage_users'))


@app.route('/add_host',methods=['POST'])
@login_required
def add_one_host():
    if not g.user.is_admin:
        return  redirect(url_for('index'))
    form = HostForm()
    if form.validate_on_submit():
        hostname=form.ipaddress.data
        password=form.password.data
        serverport=form.serverport.data
        rootpassword=form.rootpassword.data
        audit=form.audit.data
        enabled=form.enabled.data
        comment=form.comment.data
        clusterid=request.form['clusterid']
        username=Clusters.query.get(int(clusterid)).runuser
        add_token=form.add_token.data
        check_ok_count="check_ok_"+add_token
        check_fail_count="check_fail_"+add_token
        check_remain_count="check_remain_"+add_token
        ip_pool=add_host.ip_to_list(hostname)
        cache.set(check_ok_count,0)
        cache.set(check_fail_count,0)
        cache.set(check_remain_count,len(ip_pool))
        # print hostname
        # print ip_pool
        if type(ip_pool) != list:
            return ip_pool
        direct= request.form['direct']
        host_group=add_host.multi_add(ip_pool,username,password,clusterid,audit,enabled,comment,serverport,rootpassword,direct,add_token)
        result=host_group.para_add()
        # print result
        cache.delete(check_ok_count)
        cache.delete(check_fail_count)
        cache.delete(check_remain_count)
        return json.dumps(result)
    else:
        return redirect(url_for('manage_hosts'))

@app.route('/edit_one_host/<int:host_id>',methods=['POST'])
@login_required
def edit_one_host(host_id):
    if not g.user.is_admin:
        return  redirect(url_for('index'))
    form=HostForm()
    new=Hosts.query.get(int(host_id))
    if new == None:
        flash("Sorry! host with this user not found",'error')
        return redirect(url_for('manage_hosts'))
    if form.validate_on_submit():
        # username=form.username.data
        clusterid=request.form['clusterid']
        username=Clusters.query.get(int(clusterid)).runuser
        new.host_ip = form.ipaddress.data
        new.login_name = username
        new.server_port = form.serverport.data
        if form.password.data != "not_modified":
            new.login_passwd = form.password.data
        if form.rootpassword.data != "not_modified":
            new.root_passwd = form.rootpassword.data
        new.audit = form.audit.data
        new.enabled = form.enabled.data
        new.comment = form.comment.data
        new.username = username
        new.clusterid = clusterid
        if request.form['direct'] != "true":
            newhost=add_host.new_host(new.host_ip,new.login_name,new.login_passwd,new.clusterid,new.audit,new.enabled,new.comment,new.server_port,new.root_passwd)
            newhost.open()
            if newhost.result.find("checked successfully") == -1:
                return newhost.result
        try:
            db.session.commit()
            return "success"
        except Exception as e:
            app.logger.error(str(e))
            db.session.rollback()
            if str(e).find("columns host_ip, login_name are not unique") != -1:
                return "ipaddress '"+form.ipaddress.data+" with username "+form.username.data+"' is already exist in database"
            else:
                return "unknonw error,please check the log"
        finally:
            db.session.close()
    else:
        return redirect(url_for('manage_hosts'))

@app.route('/edit_multi_hosts',methods=['POST'])
@login_required
def edit_multi_hosts():
    if not  g.user.is_admin:
        return  redirect(url_for('index'))
    form=EditMultiHostForm()
    if form.validate_on_submit():
        host_list=form.host_list.data.split(',')
        action=form.action.data
        for id in host_list:
            new=Hosts.query.get(int(id))
            if action == "enable_audit":
                new.audit = True
            elif action == "disable_audit":
                new.audit = False
            elif action == "enable_hosts":
                new.enabled = True
            elif action == "disable_hosts":
                new.enabled = False
            elif action == "delete_hosts":
                db.session.delete(new)
            else:
                return "action not defined"
        try:
            db.session.commit()
            return "success"
        except Exception as e:
            app.logger.error(str(e))
            db.session.rollback()
            return "unknow error"
        finally:
            db.session.close()
    else:
        return "You have posted wrong data"


@app.route('/delete_one_host/<int:host_id>',methods=['GET'])
@login_required
def delete_one_host(host_id):
    if not g.user.is_admin:
        return  redirect(url_for('index'))
    host=Hosts.query.get(int(host_id))
    #log here
    if host == None:
        flash("Sorry! host with this user not found",'error')
        return redirect(url_for('manage_hosts'))
    db.session.delete(host)
    try:
        db.session.commit()
        flash("host "+ host.host_ip+" with user "+host.login_name +" deleted successfully",'ok')
    except Exception as e:
        app.logger.error(str(e))
        db.session.rollback()
        flash("check server's log",'error')
    finally:
        db.session.close()
    return redirect(url_for('manage_hosts'))

@app.route('/run_command',methods=['GET'])
@login_required
def run_command():
    form = RunCommandForm()
    userid = g.user.userid
    stmt = select([users_clusters.c.clusterid]).where(users_clusters.c.userid == userid)
    # print db.session.execute(stmt).fetchall()
    # enclosing_stmt=select([Hosts.host_ip]).where(Hosts.clusterid.in_(stmt))
    # print db.session.execute(enclosing_stmt).fetchall()
    if g.user.is_admin:
        hosts=Hosts.query.order_by(Hosts.full_ip).all()
        clusters=Clusters.query.all()
    else:
        hosts= Hosts.query.order_by(Hosts.full_ip).filter(Hosts.clusterid.in_(stmt)).all()
        clusters=Clusters.query.filter(Clusters.clusterid.in_(stmt)).all()
    return render_template("run_command.html",title="run command" , hosts=hosts,clusters=clusters,form=form)

@app.route('/command',methods=['POST'])
@login_required
def  command():
    form = RunCommandForm()
    userid = g.user.userid
    stmt = select([users_clusters.c.clusterid]).where(users_clusters.c.userid == userid)
    if g.user.is_admin:
        hosts=Hosts.query.all()
    else:
        hosts= Hosts.query.filter(Hosts.clusterid.in_(stmt)).all()
    permit_host=[]
    for i in hosts:
        permit_host.append(i.hostid)
    if form.validate_on_submit():
        command = form.command.data
        hostid_string = form.checked_hostid.data
        timeout = form.timeout.data
        command_token=form.command_token.data
        finished_count="finished_count_"+command_token
        cache.set(finished_count,0)
        try:
            timeout = int(timeout)
        except ValueError as e:
            return redirect(url_for('run_command'))
        if form.runuser.data == "root":
            run_as_root=True
        else:
            run_as_root=False
        ip_list=hostid_string.split(',')
        hosts_info=[]
        hosts_list=""
        for id in ip_list:
            if int(id) not in permit_host:
                return redirect(url_for('index'))
            i=Hosts.query.get(int(id))
            hosts_list=hosts_list+i.host_ip+" ,"
            hosts_info.append([i.host_ip,i.login_name,i.login_passwd,command,i.audit,i.enabled,i.server_port,i.root_passwd,run_as_root,timeout])
        command_log=Audit(g.user.userid,2,g.user.loginname+" run command "+ command + " on "+ str(len(ip_list))+" hosts: "+hosts_list)
        save_audit_log(command_log)
        run_command_cluster=run_a_command.multi_run(hosts_info,command_token)
        result_tmp=run_command_cluster.para_run()
        cache.delete(finished_count)
        try:
            result=json.dumps(result_tmp)
            return result
        except Exception as e:
            print str(e)
            return str(e)
    else:
        return redirect(url_for('run_command'))

@app.route('/register',methods=['POST'])
def register():
    form = RegisterForm()
    if form.validate_on_submit():
        loginname = form.loginname.data
        password = md5(form.password.data).hexdigest()
        realname = form.realname.data
        phonenumber = form.phonenumber.data
        email = form.email.data
        comment = form.comment.data
        try:
            permit = g.user.is_admin
        except Exception as e:
            permit = False
        if  permit:
            is_admin = form.is_admin.data
            is_locked = form.is_locked.data
            permit_clusters=form.permit_clusters.data
            if permit_clusters != "":
                permit_clusters=permit_clusters.split(",")
            else:
                permit_clusters=[]
        else:
            is_admin = False
            is_locked = True
            permit_clusters=[]
        newuser = Users(loginname,password,realname,phonenumber,email,comment=comment,is_admin=is_admin,is_locked=is_locked)
        try:
            db.session.add(newuser)
            for i in permit_clusters:
                cluster=Clusters.query.filter_by(clusterid=i).first()
                db.session.add(newuser.add_permission(cluster))
            db.session.commit()
            User=Users.query.filter_by(loginname=loginname).first()
            register_log=Audit(User.userid,1,User.realname + " registers octopus with loginname "+User.loginname)
            save_audit_log(register_log)
            return "success"
        except Exception as e:
            app.logger.error(str(e))
            db.session.rollback()
            if str(e).find("column loginname is not unique") != -1:
                return "loginname '"+loginname+"' iis already exist in database"
            elif str(e).find("column realname is not unique") != -1:
                return "realname '"+realname+"' is already exist in database"
            return "unknown error: check server's log"
        finally:
            db.session.close()
    else:
        return "you have posted illegal data"
    # return render_template("login.html",title="register" , form=form)


@app.route('/login',methods=['POST','GET'])
def login():
    loginform = LoginForm()
    registerform = RegisterForm()
    if request.method == 'POST':
        if g.user is not None and g.user.is_authenticated():
            # return redirect(url_for('index'))
            return redirect(url_for('index'))
        if loginform.validate_on_submit():
            loginname = loginform.loginname.data
            remember_me=loginform.remember_me.data
            password = md5(loginform.password.data).hexdigest()
            user=Users.query.filter_by(loginname=loginname).first()
            if user != None and user.is_locked:
                flash("your account is locked ,Please contact administrator")
                return redirect(url_for('login'))
            if user != None and user.password == password :
                login_user(user,remember=remember_me)
                login_log=Audit(g.user.userid,1,g.user.loginname + " log in to octopus")
                save_audit_log(login_log)
                return redirect(request.args.get('next') or url_for('index'))
            else:
                flash("wrong login_name or password")
                return redirect(url_for('login'))
    return render_template("login.html",title="login" , loginform=loginform,registerform=registerform)


@app.route('/logout')
@login_required
def logout():
    logout_log=Audit(g.user.userid,1,g.user.loginname + " log out from octopus")
    logout_user()
    save_audit_log(logout_log)
    return redirect(url_for('index'))

@app.route('/edit_user/<int:user_id>',methods=['POST'])
@login_required
def edit_user(user_id):
    if not g.user.is_admin :
        return  redirect(url_for('index'))
    form = RegisterForm()
    user = Users.query.get(int(user_id))
    all_cluster = Clusters.query.all()
    if user == None:
        flash("Sorry! user  not found",'error')
        return redirect(url_for('manage_users'))
    if form.validate_on_submit():
        user.is_admin = form.is_admin.data
        user.is_locked = form.is_locked.data
        user.phonenumber = form.phonenumber.data
        user.email = form.email.data
        user.comment = form.comment.data
        user.loginname = form.loginname.data
        user.realname = form.realname.data
        if form.password.data != "not_modified":
            user.password = md5(form.password.data).hexdigest()
        permit_clusters=form.permit_clusters.data
        if permit_clusters != "":
            permit_clusters=permit_clusters.split(",")
        else:
            permit_clusters=[]
        try:
            for i in permit_clusters:
                cluster=Clusters.query.filter_by(clusterid=i).first()
                db.session.add(user.add_permission(cluster))
            for cluster in all_cluster:
                if str(cluster.clusterid) not in permit_clusters:
                    db.session.add(user.del_permission(cluster))
            db.session.commit()
            return "success"
        except Exception as e:
            app.logger.error(str(e))
            db.session.rollback()
        finally:
            db.session.close()
    else:
        return "you have posted illegal data"


@app.route('/manage_commandtype')
@app.route('/manage_commandtype/<int:page>')
@login_required
def manage_commandtype(page = 1):
    if not g.user.is_admin:
        return  redirect(url_for('index'))
    form = CommandTypeForm()
    commandtypes=CommandType.query.paginate(page,COMMANDTYPE_PER_PAGE,False)
    return render_template("manage_commandtype.html",title="manage commandtype" , form=form,commandtypes=commandtypes)

@app.route('/add_commandtype',methods=['POST'])
@login_required
def add_commandtype():
    if not g.user.is_admin:
        return  redirect(url_for('index'))
    form = CommandTypeForm()
    if form.validate_on_submit():
        typename = form.typename.data
        description = form.description.data
        newcommandtype = CommandType(typename,description)
        try:
            db.session.add(newcommandtype)
            db.session.commit()
            return "success"
        except Exception as  e:
            app.logger.error(str(e))
            db.session.rollback()
            if str(e).find("column typename is not unique") != -1:
                return "commandtype '"+typename+"' is already exist in database"
            elif str(e).find("UNIQUE constraint failed: commandtype.typename"):
                return "commandtype '"+typename+"' is already exist in database"
            else:
                print "unknown error",str(e)
                return "unknonw error,please check the log"
        finally:
            db.session.close()
    else:
        return redirect(url_for("manage_commandtype"))

@app.route('/edit_commandtype/<int:type_id>',methods=['POST'])
@login_required
def edit_commandtype(type_id):
    if not g.user.is_admin:
        return  redirect(url_for('index'))
    form = CommandTypeForm()
    new = CommandType.query.get(int(type_id))
    if new == None:
        flash("Sorry! cluster  not found",'error')
        return redirect(url_for('manage_commandtype'))
    if form.validate_on_submit():
        new.typename = form.typename.data
        new.description = form.description.data
        try:
            db.session.commit()
            return "success"
        except Exception as e:
            app.logger.error(str(e))
            db.session.rollback()
            if str(e).find("column typename is not unique") != -1:
                return "commandtype '"+form.typename.data+"' is already exist in database"
            else:
                return "unknonw error,please check the log"
        finally:
            db.session.close()
    else:
        return redirect(url_for("manage_commandtype"))


@app.route('/manage_command')
@app.route('/manage_command/<int:page>')
@app.route('/manage_command/<int:page>/<int:commandtype_typeid>')
@login_required
def manage_command(page = 1,commandtype_typeid=0):
    if  not g.user.is_admin:
        return  redirect(url_for('index'))
    form = CommandForm()
    clusters=Clusters.query.all()
    commandtypes=CommandType.query.all()
    if commandtype_typeid == 0:
        hosts=Hosts.query.paginate(page,HOSTS_PER_PAGE,False)
    else:
        hosts=Hosts.query.filter_by(clusterid =commandtype_typeid).paginate(page,HOSTS_PER_PAGE,False)
    return render_template("manage_command.html",title="manage command" , form=form,hosts=hosts,clusters=commandtypes,clusterid=commandtype_typeid,commandtypes=commandtypes)


@app.route('/add_command',methods=['POST'])
@login_required
def add_command():
    if not g.user.is_admin:
        return redirect(url_for('index'))
    form=CommandForm()
    if form.validate_on_submit():
        command=form.command.data
        description=form.description.data
        typeid=request.form["typeid"]
        print command
        print description
        print typeid
        return "success"


@app.errorhandler(404)
def page_not_found(error):
    return render_template('404.html'), 404

@app.errorhandler(500)
def internal_error(error):
    db.session.rollback()
    return render_template('500.html'), 500

@app.route('/progress_bar/<string:token>',methods=['GET'])
def progress_bar(token):
    token_list=token.split("_")
    end_token=token_list[2]+"_"+token_list[3]
    if token_list[0]=="command":
        result=json.dumps({'finished_count':cache.get("finished_count_"+end_token)})
    elif token_list[0] == "add":
        result=json.dumps({'check_ok':cache.get("check_ok_"+end_token),'check_fail':cache.get("check_fail_"+end_token),"check_remain":cache.get("check_remain_"+end_token)})
    return result

# @app.route('/test')
# def test():
#     print cache.get('finished')
#     return str(cache.get('finished'))